iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0

技術筆記

本來callback function寫完,很自然地連結到解決callback hell的promise,但只理解皮毛的我實在是沒有太多的心得,還是先緩緩,先回來看閉包,其實現在想想閉包應該寫於callback甚至非同步之前的,那時三個東西卡在一起沒繞出來,又覺得不生出文章,我會沒安全感,於是跳來跳去之後,跳到覺得自己不能再跳了之後,硬是把非同步跟callback先生出來。

閉包

  • Closures in JS 🔥 | Namaste JavaScript Episode 10
    跟大家分享這個youtube影片,我是用點閱數篩選找到他的,雖然他有一點印度腔,但沒有很難懂,我覺得他講的閉包蠻清楚的,而且很興奮很熱情,看影片看得精神振奮XD決定直接沿用他的例子,去解釋我從影片中理解到的閉包。
  • 簡單一句話,閉包就是一個函式連同他的語彙環境
//最簡單的閉包範例
function x(){
var a = 7
function y(){
console.log(a)
}
y()
}

x()

//7
function x(){
var a = 7
function y(){
console.log(a)
}
return y
}

var z = x()
console.log(z)
z()

如果把上述程式碼丟去devtool會回傳如下訊息
https://ithelp.ithome.com.tw/upload/images/20231004/20163234K3vh8A4RET.png
從console.log(z)回傳函式y且呼叫z回傳7,可以看出z不只記憶了函式y,還連同它的語彙環境,所以呼叫z才有辦法回傳7,換句話說,z記憶的就是因為y而產生的整個閉包。

  • 進階範例:setTimeout
function outer() {
  var a = 10
  setTimeout(function () {
    console.log(a);
  }, 3000)
  console.log(`hello`)
}
outer()

//hello

//10

這個setTimeout同樣也是個閉包,所以在計時結束後,callback放入queue再等callstack空了之後進到callstack,根據語彙環境找到a=10接著印出10。

  • 進階範例:for迴圈 每隔一秒印出1~5
function print() {
  for (var i = 1; i <= 5; i++) {
    setTimeout(function () {
      console.log(i);
    }, i * 1000)
  }
}

print()

//6
//6
//6
//6
//6
  • 錯誤原因:因為setTimeout是一個閉包,而它會放到eventTable倒數,而callStack並不會停下,所以for迴圈持續跑到最後,停在6。等setTimeout倒數完後執行函式時(中間過程如上不贅述),回頭根據reference尋找i會找到6,於是出來的結果變成每隔一秒印出6,印5次
  • 解法1:把var改成let,利用let為區塊範疇的特性,每跑一次i,會有不同的reference,所以能分別印出1.2.3.4.5
function print() {
  for (let i = 1; i <= 5; i++) {
    setTimeout(function () {
      console.log(i);
    }, i * 1000)
  }
}

print()

//1
//2
//3
//4
//5
  • 解法2: 再用一個函式(closure)把setTimeout包裝起來,透過呼叫closure(i),再進去跑迴圈,每次setTimeout形成的閉包對應到的都是當次的i值
function print() {
  for (var i = 1; i <= 5; i++) {
    function closure(i){
      setTimeout(function () {
        console.log(i);
      }, i * 1000)      
    }
    closure(i)
  }  
}

print()

//1
//2
//3
//4
//5
//跟上面是一樣的,只是改這樣讓我看得比較清楚
function print() {
  for (var i = 1; i <= 5; i++) {
    closure(i)
  }
}

function closure(i) {
  setTimeout(function () {
    console.log(i);
  }, i * 1000)
}

print()

//1
//2
//3
//4
//5

心得

結果我這兩天最大的收穫跟主題沒關係...而是我終於搞懂了函式本身跟函式呼叫,哈哈哈哈哈哈,一個好像很基本的東西,從昨天huli老師提醒初學者callback容易犯的錯,那時寫筆記,覺得懂,但今天在影片看完,自己實作第一個踩的坑,就是我把y()寫成y。雖然明明老師提醒過,我還是跌進坑裡,不過沒關係,真的跌進去爬出來後,我才真的深深體會到兩個的不同,也是很開心。
閉包透過閱讀這幾隻影片後,有比較深入的了解,雖然試著敘述給自己的的時候,還是卡卡的,我想還需要時間,另外令我振奮的是他的熱情及他對JavaScript的熱愛,感染了我,讓我感到學習是件很享受的事。

參考資料

童言童語

努力看完天書後,來點輕鬆的吧!分享我兒子的童言童語,調劑身心一下

5歲樂咖+2歲嗨啾 = 我的神奇寶貝 皮咖啾

阿咖有一天下課跟我分享了康丁斯基的畫
我心想好有藝術氣息喔他
難得上課這麼認真
結果⋯
咖:他是康丁斯基,我是許XX司機⋯


上一篇
Day18 Callback
下一篇
Day20 例外處理(try...catch)
系列文
豆芽班日記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言